home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / addpcx.zip / IBMTIMER.C < prev    next >
C/C++ Source or Header  |  1988-06-21  |  5KB  |  204 lines

  1. /*    IBMTIMER - stopwatch and timer functions for the IBMPC.
  2.  
  3.     This stuff is so system specific that there's no point
  4.     in calling it directly from an application.
  5. */
  6.  
  7.  
  8. #include <ibmtimer.h>
  9.  
  10.  
  11. /*    IBMPC_STOPWATCH
  12.  
  13.     This function implements a simple and very accurate stopwatch.
  14.     NOTE that this function must be called at least once per half
  15.     hour in order for it to remain accurate. In fact, it will spin
  16.     in an almost infinite loop if called less than once per half
  17.     hour. Also, for long duration events (greater than a minute,)
  18.     it is much better to compute elapsed time using the system
  19.     clock (as apposed to the DOS time function, which is based on
  20.     the system tick counter and is not accurate at all.
  21.  
  22.     The information required by the stopwatch for each event being
  23.     timed is kept in an IBM_STOPWATCH struct, the structure for
  24.     which is declared in "ibmtimer.h"
  25.  
  26.     The first parameter is a reset flag. The second is a pointer
  27.     to the stopwatch struct for the event being timed. If the
  28.     reset flag is true, the stopwatch struct is reset to zero,
  29.     and timing begins. If the reset flag is zero, then the
  30.     data in the stopwatch struct is updated, and the elapsed time
  31.     from the reset operation to the current operation is returned.
  32.  
  33.     The return value is always the number of COUNTER increments
  34.     since the last reset operation. ie: 1 second = 1193180 counter
  35.     increments.
  36.  
  37.     Operation is as follows:
  38.  
  39.     The 8253 Timer channel 0 is configured as a down counter which
  40.     counts 65536 transitions on its clock line, then generates an
  41.     interrupt. The interrupt is handled by a routine called TIMER_INT
  42.     (see page A-79 of the Tech. Ref. PC-XT.) This routine increments
  43.     an int var called TIMER_LOW. Since the input clock to timer channel 
  44.     is running at 1193180 Hz, TIMER_LOW is incremented at the rate of 
  45.     1193180 / 2^16 = 18.20648193 times per second. At that rate, it 
  46.     rolls over once every 2^16 / 18.2 = 3599.597124 seconds, or approx. 
  47.     once per hour. The error is 111.91us per second on a system
  48.     with a perfectly adjusted crystal (very rare.)
  49.  
  50.     This stopwatch works by computing the elapsed time
  51.     since the reset of the stopwatch struct using the TIMER_LOW
  52.     var and the 8253 channel 0 counter.
  53.  
  54.     Note that due to the problem of reading the DOS time & the
  55.     timer channel count while same are being updated, it is
  56.     possible that a read error of several microseconds may occur.
  57.     To prevent such invalid values from being returned by this
  58.     routine, a check is made for samples that are more than 30
  59.     minutes apart (that's what the errors look like.) In the
  60.     event that such a sample is detected, another sample is
  61.     taken. As such, this routine could go into a dead spin for a half
  62.     hour if it is not called more than once every half hour.
  63. */
  64.  
  65.  
  66. unsigned long ibmpc_stopwatch( resflg, p )
  67. struct IBMPC_STOPWATCH *p;
  68. int resflg;    /* reset flag    */
  69. {
  70.     static unsigned long read_ibmpc_time();
  71.     unsigned long c;
  72.  
  73. rderr:    c = read_ibmpc_time();
  74.  
  75.     if ( resflg )
  76.     {  p->last_count = c;
  77.        p->elapsed_time = 0l;
  78.     };
  79.  
  80.     if ( (c - p->last_count) & 0x80000000l )
  81.        goto rderr;
  82.  
  83.     p->elapsed_time += (c - p->last_count);
  84.     p->last_count = c;
  85.  
  86.     return p->elapsed_time;
  87. }
  88.  
  89.  
  90. /*    READ_IBMPC_TIME
  91.  
  92.     Returns the current value of TIMER_LOW as the high order 16 bits
  93.     of its result, and the current count in the 8253 timer channel 0
  94.     as the low order 16 bits.
  95. */
  96.  
  97. unsigned long read_ibmpc_time()
  98. {
  99. #ifndef _lint
  100. #asm
  101. timer    equ    040h
  102.  
  103.     ;
  104.     ;routine to return TIMER_LOW:Channel Count
  105.     ;
  106.  
  107.     push    ds        ;save current segment
  108.     mov    ax, 040h    ;use segment at 40h
  109.     mov    ds, ax
  110.     mov    bx, 06ch    ;get offset to TIMER_LOW
  111.  
  112.     mov    al, 0        ;ready to read timer
  113.     out    timer+3,al    ;this latches the current count
  114.  
  115.     cli            ;disable interrupts
  116.  
  117.     mov    dx, [bx]    ;get TIMER_LOW
  118.  
  119.     in    al, timer+0    ;read the current count
  120.     mov    ah, al
  121.     in    al, timer+0
  122.  
  123.     sti            ;re-enable interrupts
  124.  
  125.     xchg    al, ah
  126.  
  127.     mov    cx, ax        ;save it
  128.     xor    ax, ax        ;clear ax
  129.     sub    ax, cx        ;convert to positive count
  130.  
  131.     pop    ds        ;restore DS
  132. #endasm
  133. #else
  134.     unsigned long happy_lint;
  135.  
  136.     happy_lint = 123l;
  137.  
  138.     return happy_lint;
  139. #endif
  140. }
  141.  
  142.  
  143. /*    READ_IBMPC_TIMER
  144.  
  145.     This function simply reads the current value in
  146.     the 8253 channel 0.
  147. */
  148.  
  149. unsigned int read_ibmpc_timer()
  150. {
  151. #ifndef    _lint
  152. #asm
  153.     mov    al, 0h        ;set up to read low/high count
  154.     out    timer+3,al
  155.  
  156.     in    al, timer+0    ;read the current count
  157.     mov    ah, al
  158.     in    al, timer+0
  159.     xchg    al, ah
  160.  
  161.     mov    cx, ax        ;save it
  162.     xor    ax, ax        ;clear ax
  163.     sub    ax, cx        ;convert to positive count
  164. #endasm
  165. #else
  166.     unsigned int happy_lint;
  167.  
  168.     happy_lint = 123;
  169.  
  170.     return happy_lint;
  171. #endif
  172. }
  173.  
  174.  
  175. /*    CVT_IBMPC_TIME
  176.  
  177.     This function converts any of the time values above
  178.     into Minutes, Seconds & Thousandths of seconds.
  179.     It returns as its value the time passed to it.
  180. */
  181.  
  182. unsigned long cvt_ibmpc_time( time, pm, ps, phs )
  183. unsigned long time;
  184. unsigned int *pm, *ps, *phs;
  185. {
  186.     unsigned long t, m, s, hs;
  187.     static unsigned long timerf = IBMPC_TIMER0_FREQ;
  188.  
  189.     s  = time / timerf;    /* total number of seconds    */
  190.     t  = time - (s * timerf);
  191.  
  192.     m  = s / 60l;        /* number of minutes        */
  193.     s -= m * 60l;        /* remainder is s        */
  194.  
  195.     hs = (t * 1000l) / timerf;
  196.  
  197.     *pm = (unsigned int)  m;
  198.     *ps = (unsigned int)  s;
  199.     *phs= (unsigned int) hs;
  200.  
  201.     return time;
  202. }
  203.  
  204.